﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml.Linq;

namespace System
{
	public static class XElementExtension
	{
		private static readonly Dictionary<Type, MethodInfo> op_XElementConvert =
			new Dictionary<Type, MethodInfo>();

		static XElementExtension()
		{
			foreach (var item in typeof(XElement).GetMethods().Where(s => s.Name == "op_Explicit" && s.IsSpecialName && s.IsStatic && !op_XElementConvert.ContainsValue(s)))
				op_XElementConvert.Add(item.ReturnType, item);
		}

		public static T GetOrDefaultValued<T>(this XElement xe, XName name)
			where T : struct
		{
			return (xe != null && name != null)
			       	? xe.Element(name).GetOrDefaultValued<T>()
			       	: default(T);
		}

		public static T GetOrDefaultValued<T>(this XElement xe)
			where T : struct
		{
			Nullable<T> nl;
			try
			{
				if (xe != null && (nl = (Nullable<T>)op_XElementConvert[typeof(T)].Invoke(null, new object[] { xe })).HasValue)
					return nl.Value;
				else
					return default(T);
			}
			catch
			{
				return default(T);
			}
		}

		/// <summary>
		/// Приводит XElement к string и возвращает подстроку не больше указанной длины
		/// </summary>
		/// <param name="xe">XElement</param>
		/// <param name="len">Ограничитель длины строки</param>
		/// <returns>"" или значение</returns>
		public static string GetSubstring(this XElement xe, int len)
		{
			var strk = "";
			if (xe != null)
			{
				strk = (string)xe ?? "";
				if (strk.Length > len)
					strk = strk.Substring(0, len);
			}
			return strk;
		}

		public static int GetInt(this XElement xe)
		{
			int i =
				xe != null && int.TryParse(xe.Value, out i)
															? i : 0;
			return i;
		}

		public static double GetDouble(this XElement xe)
		{
			double d = 0;
			if (xe != null && !double.TryParse(xe.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out d))
				double.TryParse(xe.Value, NumberStyles.Any, CultureInfo.CurrentCulture, out d);
			return d;
		}		

		public static DateTime? GetOrDefaultDateTime(this XElement xe, params string[] formats)
		{
			DateTime? dtn = null;
			var dt = DateTime.MinValue;
			if (formats != null && xe != null
				&& DateTime.TryParseExact(xe.Value, formats,
					CultureInfo.InvariantCulture, DateTimeStyles.None, out dt))
				dtn = dt;
			if (!dtn.HasValue)
				try
				{
					if (xe.Value.Length > 12)
					{
						if (xe.Value.IndexOf("/") > 0)
							dtn = DateTime.ParseExact(xe.Value, "MM/dd/yyyy hh:mm:ss tt", CultureInfo.InvariantCulture);
						else
							if (xe.Value.IndexOf("-") > 0 && xe.Value.IndexOf("T") > 0)
								dtn = DateTime.ParseExact(xe.Value, "yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture);
							else
								if (xe.Value.IndexOf("-") > 0)
									dtn = DateTime.ParseExact(xe.Value, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
								else
									if (xe.Value.IndexOf(".") > 3)
										dtn = DateTime.ParseExact(xe.Value, "yyyy.MM.dd HH:mm:ss", CultureInfo.InvariantCulture);
									else
										dtn = DateTime.ParseExact(xe.Value, "dd.MM.yyyy HHh:mm:ss", CultureInfo.InvariantCulture);

						//dtn = (DateTime?)xe;
					}
					else
					{
						if (xe.Value.IndexOf("/") > 0)
							dtn = DateTime.ParseExact(xe.Value, "MM/dd/yyyy", CultureInfo.InvariantCulture);
						else
							if (xe.Value.IndexOf("-") > 0)
								dtn = DateTime.ParseExact(xe.Value, "yyyy-MM-dd", CultureInfo.InvariantCulture);
							else
								if (xe.Value.IndexOf(".") > 3)
									dtn = DateTime.ParseExact(xe.Value, "yyyy.MM.dd", CultureInfo.InvariantCulture);
								else
									dtn = DateTime.ParseExact(xe.Value, "dd.MM.yyyy", CultureInfo.InvariantCulture);

						//dtn = (DateTime?)xe;
					}

				}
				catch
				{
					dtn = null;
				}
			return dtn;
		}

		public static DateTime GetOrDefaultDateTime(this XElement xe, DateTime defValue, params string[] formats)
		{
			DateTime? dtn = xe.GetOrDefaultDateTime(formats);
			return dtn.HasValue ? dtn.Value : defValue;
		}
	}
}
